<?php
require_once 'class/meta/Authorizer.php';
require_once 'class/action/Command.php';
/**
 * Pipeline class, store and retrieve commands.
 * @static
 * @package meta
 * @author alcaitiff
 */

class Pipeline
{
	private static $tickSize=null;
	private static $coreTick=null;
	private static $characterTick=null;
	
    /**
     * Store a command to execution
     * @param string $charID Character name
     * @param string $command Command string
     * @return void
     * @static
     */
	static public function addCommand($charID,$command){
		if($command!=null){
			$command=trim($command);
			if(!self::verifyCommand($command)){
				MessageSystem::addDebugMessage('Invalid command');
			}elseif($charID==null){
				MessageSystem::addDebugMessage('Invalid character. Command:'.$command.' not saved');
			}else{
				self::saveCommand($charID,$command);				
			}
		}
	}
    /**
     * Store commands to execution
     * @param string $charID Character name
     * @param array $commands Commands
     * @return void
     * @static
     */
	static public function addCommands($charID,$commands){
		foreach($commands as $command){
			if($command!=null)
				self::addCommand($charID,$command);
		}
	}
	
    /**
     * Persist the command on the pipe
     * @param string $charID Character name
     * @param string $line Command line
     * @return void
     * @static
     */
	static public function saveCommand($charID,$line){
		//Verify if all variables are set and if it's need set them.
		self::setVariables($charID);
		self::$characterTick+=self::$tickSize;
		$commands=explode(' ',$line);
		MessageSystem::addDebugMessage('Add command { characterId:'.$charID.',command:'.$commands[0].',ts:'.self::$characterTick.'}');
		self::storeCommand($charID,self::$characterTick,$commands[0],implode(' ',array_slice($commands,1)));
	}
    /**
     * Set tick size, core tick and character tick
     * @param string $charID Character name
     * @return void
     * @static
     */
	static private function setVariables($charID){
		if(self::$tickSize==null){
			self::$tickSize=Ticker::getTickSize();
		}
		if(self::$coreTick==null){
			self::$coreTick=Ticker::getCurrentTick();
		}
		//If the character have no commands in the pipeline his last tick become the core tick
		if(self::$characterTick==null){
			self::$characterTick=self::getCharacterLastTick($charID);
			if(self::$characterTick==null){
				self::$characterTick=self::$coreTick-self::$tickSize;
			}
		}
		if(self::$characterTick<self::$coreTick-self::$tickSize){
			self::$characterTick=self::$coreTick-self::$tickSize;
		}
		
	}	
	/**
     * Retrieve the timestamp of oldest command on pipe
     * @return int
     * @static
     */
	static public function getOldestCommand(){
		return self::retrieveOldestCommand();
	}
	/**
     * Get commands for timestamp $ts
     * @param int $ts Timestamp
     * @return array
     * @static
     */
	static public function getCommands($ts){
		$ret=self::retrieveCommands($ts);
		self::removeCommands($ts);
		return $ret;
	}
    //***********************************************************************************************************
    //					DATABASE METHODS
    //***********************************************************************************************************
	/**
     * Retrieve last tick for this character
     * @param string $char Character name
     * @return int 
     * @static
     */
    static private function getCharacterLastTick($char){
			$ts=null;
			$dbo=new DB();
			$db=$dbo->db;
			$stmt = $db->prepare(	'SELECT UNIX_TIMESTAMP(timestamp) '.
									'FROM pipeline '.
									'WHERE '.
									'`char_id` = ? '.
									'ORDER BY timestamp DESC '.
									'LIMIT 1'
									);
		    $stmt->bind_param('s', $char);
		    $stmt->bind_result($ts);
		    $stmt->execute();
    		$stmt->fetch();
			$stmt->close(); 
			return $ts;
	}

    /**
     * Validate the command
     * @param string $line Command line
     * @return boolean
     * @static
     */
	static private function verifyCommand($line){
		if($line==null)
			return false;
		else{
			$command=explode(' ',$line);
			MessageSystem::addDebugMessage('Verifying Command: '.$command[0]);
			$dbo=new DB();
			$db=$dbo->db;
			$stmt = $db->prepare(	'SELECT command '.
									'FROM commands '.
									'WHERE '.
										'command = ? '
									);
		    $stmt->bind_param('s', $command[0]);
		    $stmt->execute();
    		$result=$stmt->fetch();
			$stmt->close(); 

			return $result;

		}	
	}
    /**
     * Save a command
     * @param string $char Character name
     * @param int $ts Timestamp
     * @param string $command Command line
     * @param string $parameters Coma separated parameters in a single string
     * @return void
     * @static
     */
	static private function storeCommand($char,$ts,$command,$parameters){
		$dbo=new DB();
		$db=$dbo->db;
		$stmt = $db->prepare(	'INSERT INTO pipeline '.
								'(`char_id`,timestamp,command,parameters) '.
								'VALUES '.
								'(?,FROM_UNIXTIME(?),?,?)'
									);
		$stmt->bind_param('ssss',$char,$ts,$command,$parameters);								
	    $stmt->execute();
	    $rows=$stmt->affected_rows;
	    $stmt->close();
	}
	/**
     * Retrieve commands for $time
     * @param int $time Timestamp
     * @return array|Command[]
     * @static
     */
	static private function retrieveCommands($time){
		$ret=array();
		$dbo=new DB();
		$db=$dbo->db;
		$stmt = $db->prepare(	'SELECT `char_id`,UNIX_TIMESTAMP(timestamp),commands.command,parameters,execution_time '.
								'FROM pipeline,commands '.
								'WHERE (timestamp<=FROM_UNIXTIME(?) and commands.command=pipeline.command) '.
								'ORDER BY timestamp,execution_time'
									);
		$stmt->bind_param('i',$time);
	    $stmt->bind_result($character,$ts,$command,$parameters,$executionTime);
	    $stmt->execute();
	    while($stmt->fetch()){
	    	$ret[]=new Command($character,$ts,$command,$parameters,$executionTime);
	    }
	    $stmt->close();
	    return $ret;
	}
    /**
     * Retrieve the oldest command
     * @return int
     * @static
     */
	static private function retrieveOldestCommand(){
		$ret=null;
		$dbo=new DB();
		$db=$dbo->db;
		$stmt = $db->prepare(	'SELECT UNIX_TIMESTAMP(min(timestamp)) '.
								'FROM pipeline '
									);
	    $stmt->bind_result($ret);
	    $stmt->execute();
	    $stmt->fetch();
	    $stmt->close();
	    return $ret;
	}
    /**
     * Remove all commands older than $time
     * @param int $time Timestamp
     * @return void
     * @static
     */
	static private function removeCommands($time){
		$dbo=new DB();
		$db=$dbo->db;
		$stmt = $db->prepare(	'DELETE '.
								'FROM pipeline '.
								'WHERE (timestamp<=FROM_UNIXTIME(?)) '
									);
		$stmt->bind_param('i',$time);
	    $stmt->execute();
	    $stmt->close();
	}
}